package cn.opencodes.framework.core.redis;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import cn.opencodes.framework.tools.utils.SpelUtil;

/**
 * 分布式redis锁切面实现 (不同服务器上时间差会造成锁被其他线程提前释放)
 * 
 * @author HJ
 */
@Aspect
public class RedisLockAspect {
	private static final String LOCK_KEY = "RedisLock:";

	private final Logger log = LoggerFactory.getLogger(getClass());

	@Autowired
	private RedisDistributedLock redisUtils;

	@Pointcut("@annotation(cn.opencodes.framework.core.redis.RedisLock)")
	private void lockPoint() {
	}

	@Around("lockPoint()")
	public Object arround(ProceedingJoinPoint point) throws Throwable {
		Object obj = null;
		RLock lock = null;
		boolean success = false;

		try {
			RedisLock lockInfo = getLockAnnotation(point);
			String lockKey = getRedisKey(point, lockInfo.value());
			// 获取锁
			//requestId = redisUtils.lock(lockKey, lockInfo.keepMills(), lockInfo.lockFailMax(), lockInfo.sleepMillis());
			lock = redisUtils.getLock(lockKey);
			success = redisUtils.lock(lock, lockInfo.keepMills(), lockInfo.lockFailMax(), lockInfo.sleepMillis());
			if (success) {
				log.debug("请求得到锁===>{}", lockKey);
				obj = point.proceed();
			}else {
				log.debug("请求锁失败===>{}", lockKey);
			}
		} catch (Exception e) {
			log.error("请求锁异常:", e);
		}finally {
			if (lock != null && success) {
				redisUtils.unlock(lock);
				log.debug("释放请求锁===>{}", lock.getName());
			}
		}

		return obj;
	}

	/*
	 * private boolean getLockState(String lockKey, long lockTimeout, boolean
	 * lockSuccess, RedisLock lockInfo){ long failMaxNum =
	 * lockInfo.lockFailMax(); int failNum = 0; while(!lockSuccess &&
	 * failNum++<failMaxNum){ lockSuccess = redisUtils.lock(lockKey,
	 * lockTimeout); if(lockSuccess ||
	 * lockInfo.action().equals(LockFailAction.GIVEUP)){ break; }else{
	 * log.debug("重新请求锁===>{}", lockKey); } } return lockSuccess; }
	 */
	private RedisLock getLockAnnotation(ProceedingJoinPoint point)
			throws NoSuchMethodException {
		MethodSignature signature = (MethodSignature) point.getSignature();
		Method method = point.getTarget().getClass()
				.getMethod(signature.getName(), signature.getParameterTypes());
		RedisLock lockInfo = method.getAnnotation(RedisLock.class);
		return lockInfo;
	}

	/**
	 * 将spel表达式转换为字符串
	 * 
	 * @param joinPoint
	 *            切点
	 * @return redisKey
	 */
	private String getRedisKey(ProceedingJoinPoint joinPoint, String spel) {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method targetMethod = methodSignature.getMethod();
		Object target = joinPoint.getTarget();
		Object[] arguments = joinPoint.getArgs();
		return LOCK_KEY + SpelUtil.parse(target, spel, targetMethod, arguments);
	}
}